/**
 * @file scanner.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2024-03-30
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#ifndef __JSASM_SCANNER_H__
#define __JSASM_SCANNER_H__

#include <algorithm>
#include <memory>
#include <iostream>
#include "src/base/strings.h"
#include "src/parsing/token.h"

class Utf16CharacterStream {
    public:
        static constexpr base::uc32 kEndOfInput = static_cast<base::uc32>(-1);

        virtual ~Utf16CharacterStream() = default;

        JSASM_INLINE void set_parser_error() {
            buffer_cursor_ = buffer_end_ + 1;
            has_parser_error_ = true;
        }

        JSASM_INLINE void reset_parser_error_flag() { has_parser_error_ = false; }

        JSASM_INLINE bool has_parser_error() const { return has_parser_error_; }

        inline base::uc32 Peek() {
            if (JSASM_LIKELY(buffer_cursor_ < buffer_end_)) {
                return static_cast<base::uc32>(*buffer_cursor_);
            } else if (ReadBlockChecked(pos())) {
                return static_cast<base::uc32>(*buffer_cursor_);
            } else {
                return kEndOfInput;
            }
        }
        
        inline base::uc32 Advance() {
            std::cout << "inline base::uc32 Advance:" << buffer_cursor_ << std::endl;
            base::uc32 result = Peek();
            std::cout << "result\t" << (char)result << std::endl;
            buffer_cursor_++;
            return result;
        }

        // template <typename FunctionType>
        // JSASM_INLINE base::uc32 AdvanceUntil(FunctionType check) {
        //     while(true) {
        //         auto next_cursor_pos = 
        //             std::find_if(buffer_cursor_, buffer_end_, [&check](uint16_t raw_c0_) {
        //                 base::uc32 c0_ = static_cast<base::uc32>(raw_c0_);
        //                 return check(c0_);
        //             });
                
        //         if (next_cursor_pos == buffer_end_) {
                    
        //         }
        //     }
        // }

        JSASM_INLINE void Back() {
            if (JSASM_LIKELY(buffer_cursor_ > buffer_start_)) {
                buffer_cursor_--;
            } else {
                ReadBlockChecked(pos() - 1);
            }
        }

        JSASM_INLINE size_t pos() const {
            return buffer_pos_ + (buffer_cursor_- buffer_start_);
        }
        
         virtual void PrintData() = 0;

    protected:
        Utf16CharacterStream(const uint16_t* buffer_start,
            const uint16_t* buffer_cursor,
            const uint16_t* buffer_end, size_t buffer_pos)
            : buffer_start_(buffer_start),
              buffer_cursor_(buffer_cursor),
              buffer_end_(buffer_end),
              buffer_pos_(buffer_pos) {
                std::cout << buffer_pos << "\tUtf16CharacterStream" << std::endl;
              }
        
        Utf16CharacterStream() : Utf16CharacterStream(nullptr, nullptr, nullptr, 0) {} 

        bool ReadBlockChecked(size_t position) {
            DCHECK(position < buffer_pos_ || position >= buffer_pos_ + (buffer_end_ - buffer_start_));
            std::cout << "ReadBlockChecked" << std::endl;
            bool success = ReadBlock(position);
            return success;
        }

        virtual std::unique_ptr<Utf16CharacterStream> Clone() const = 0;

        virtual bool ReadBlock(size_t position) = 0;

       
        const uint16_t* buffer_start_;
        const uint16_t* buffer_cursor_;
        const uint16_t* buffer_end_;
        size_t buffer_pos_;
        
        bool has_parser_error_ = false;
};

class  Scanner {
    public:
        class JSASM_NODISCARD BookmarkScope {
            public:
                explicit BookmarkScope(Scanner* scanner) : 
                    scanner_(scanner) {

                }

            private:
                static const size_t kNoBookmark;

                static const size_t kBookmarkWasApplied;

                Scanner* scanner_;

                size_t bookmark_;

                bool had_parser_error_;
        };

        JSASM_INLINE void set_parser_error() {
            // if () {

            // }
        }

        JSASM_INLINE void reset_parser_error_flag() {

        }

        JSASM_INLINE bool has_parser_error() const {
            return source_->has_parser_error();
        }

        struct Location {
            Location(int b, int e) : beg_pos(b), end_pos(e) {}
            Location() : beg_pos(0), end_pos(0) {}

            int length() const { return end_pos - beg_pos; }
            bool IsValid() const { return base::IsInRange(beg_pos, 0, end_pos); }

            static Location invalid() { return Location(-1, 0); }

            int beg_pos;
            int end_pos;
        };

        explicit Scanner(Utf16CharacterStream* source);

        void Initialize();

        void PrintData() {
            source_->PrintData();
        }

    private:

        class ErrorState;

        struct TokenDesc {
            Location location = {0, 0};
            Token::Value token = Token::UNINITIALIZED;
            Location invalid_template_escape_location;
            uint32_t smi_value_ = 0;
            bool after_line_terminator = false;
        };

        enum NumberKind {
            IMPLICIT_OCTAL,
            BINARY,
            OCTAL,
            HEX,
            DECIMAL,
            DECIMAL_WITH_LEADING_ZERO
        };

        JSASM_INLINE bool IsValidBigIntKind(NumberKind kind) {
            return base::IsInRange(kind, BINARY, DECIMAL);
        }

        JSASM_INLINE bool IsDecimalNumberKind(NumberKind kind) {
            return base::IsInRange(kind, DECIMAL, DECIMAL_WITH_LEADING_ZERO);
        }

        static const int kCharacterLookaheadBufferSize = 1;
        static const int kMaxAscii = 127;

        template <bool capture_raw>
        base::uc32 ScanOctalEscape(base::uc32 c, int length);

        void Init() {
            Advance();

            current_ = &token_storage_[0];
            next_ = &token_storage_[1];
            next_next_ = &token_storage_[2];
        }

        // void ReportScannerError(const Location& location, MessageTemplate error) {

        // }

        void SeekNext(size_t position);

        // JSASM_INLINE void AddLiteralChar(base::uc32 c) {
        //     next().literal_chars.AddChar(c);
        // }

        template <bool capture_raw = false>
        void Advance() {
            if (capture_raw) {
                //AddRawLitertalChar(c0_);
            }

            c0_ = source_->Advance();
        }

        JSASM_INLINE Token::Value ScanSingleToken();

        JSASM_INLINE void Scan();

        JSASM_INLINE void Scan(TokenDesc* next_desc);

        JSASM_INLINE Token::Value SkipWhiteSpace();

        int source_pos() {
            return static_cast<int>(source_->pos()) - kCharacterLookaheadBufferSize;
        }

        TokenDesc& next() { return *next_; }

        const TokenDesc& current() const { return *current_; }

        const TokenDesc& next() const { return *next_; }

        const TokenDesc& next_next() const { return *next_next_; }
        
        TokenDesc* current_;

        TokenDesc* next_;

        TokenDesc* next_next_;

        Utf16CharacterStream* const source_;

        base::uc32 c0_;

        TokenDesc token_storage_[3];

        bool found_html_comment_;

        // LiteralBuffer source_url_;

        // LiteralBuffer source_mapping_url_;

        // Location octal_pos_;
        // MessageTemplate octal_message_;

        // MessageTemplate scanner_error_;
        // Location scanner_error_location_;
};

#endif // __JSASM_SCANNER_H__